import os
from ament_index_python.packages import get_package_share_directory

from launch import LaunchDescription
from launch.actions import DeclareLaunchArgument, GroupAction, SetEnvironmentVariable
from launch.conditions import IfCondition
from launch.substitutions import LaunchConfiguration, PythonExpression
from launch_ros.actions import Node, ComposableNodeContainer
from launch_ros.descriptions import ComposableNode, ParameterFile
from nav2_common.launch import RewrittenYaml


def generate_launch_description():
    bringup_dir = get_package_share_directory('b2_nav_bringup')
    
    mask_yaml_files = os.path.join(bringup_dir, 'map', 'DAM-563_11.yaml') 

    namespace = LaunchConfiguration('namespace')
    map_yaml_file = LaunchConfiguration('map')
    use_sim_time = LaunchConfiguration('use_sim_time')
    autostart = LaunchConfiguration('autostart')
    params_file = LaunchConfiguration('params_file')
    log_level = LaunchConfiguration('log_level')
    
    points_topic = LaunchConfiguration('points_topic')
    imu_topic = LaunchConfiguration('imu_topic')
    globalmap_pcd = LaunchConfiguration('globalmap_pcd')

    lifecycle_nodes = ['map_server', 'filter_mask_server', 'costmap_filter_info_server']
    # lifecycle_nodes = ['map_server']

    remappings = [('/tf', 'tf'),
                  ('/tf_static', 'tf_static')]

    param_substitutions = {
        'use_sim_time': use_sim_time,
        'yaml_filename': map_yaml_file}

    configured_params = ParameterFile(
        RewrittenYaml(
            source_file=params_file,
            root_key=namespace,
            param_rewrites=param_substitutions,
            convert_types=True),
        allow_substs=True)

    stdout_linebuf_envvar = SetEnvironmentVariable(
        'RCUTILS_LOGGING_BUFFERED_STREAM', '1')

    declare_namespace_cmd = DeclareLaunchArgument(
        'namespace', default_value='', description='Top-level namespace')

    declare_map_yaml_cmd = DeclareLaunchArgument(
        'map',
        default_value=os.path.join(bringup_dir, 'map', 'DAM-563.yaml'),
        description='Full path to 2D map yaml file to load (for Costmap)')

    declare_use_sim_time_cmd = DeclareLaunchArgument(
        'use_sim_time', default_value='false', description='Use simulation clock')

    declare_params_file_cmd = DeclareLaunchArgument(
        'params_file',
        default_value=os.path.join(bringup_dir, 'params', 'nav2_params.yaml'),
        description='Full path to the ROS2 parameters file to use for all launched nodes')

    declare_autostart_cmd = DeclareLaunchArgument(
        'autostart', default_value='true', description='Automatically startup the nav2 stack')

    declare_log_level_cmd = DeclareLaunchArgument(
        'log_level', default_value='info', description='log level')

    # --- HDL---
    declare_points_topic_cmd = DeclareLaunchArgument(
        'points_topic', default_value='/rslidar_points_synced', description='LiDAR topic')
        
    declare_imu_topic_cmd = DeclareLaunchArgument(
        'imu_topic', default_value='/dog_imu_raw_synced', description='IMU topic')
        
    declare_globalmap_pcd_cmd = DeclareLaunchArgument(
        'globalmap_pcd',
        default_value='src/hdl_Localization_ROS2/hdl_localization/data/DAM-563-Original-merge-lite-5cm-ascii.pcd',
        description='Full path to 3D PCD map file')


    load_map_server = Node(
        package='nav2_map_server',
        executable='map_server',
        name='map_server',
        output='screen',
        respawn=True,
        respawn_delay=2.0,
        parameters=[configured_params],
        arguments=['--ros-args', '--log-level', log_level],
        remappings=remappings)
    
    start_filter_mask_server = Node(
        package='nav2_map_server',
        executable='map_server',
        name='filter_mask_server',
        output='screen',
        emulate_tty=True,
        parameters=[{
            'use_sim_time': use_sim_time,
            'yaml_filename': mask_yaml_files
        }],
        remappings=[
            ('/map', '/filter_mask')
        ]
    )

    start_costmap_filter_info_server = Node(
        package='nav2_map_server',
        executable='costmap_filter_info_server',
        name='costmap_filter_info_server',
        output='screen',
        emulate_tty=True,
        parameters=[{
            'use_sim_time': use_sim_time,
            'type': 1,
            'filter_info_topic': '/costmap_filter_info',
            'mask_topic': '/filter_mask',
            'base': 0.0,
            'multiplier': 1.0
        }]
    )

    load_lifecycle_manager = Node(
        package='nav2_lifecycle_manager',
        executable='lifecycle_manager',
        name='lifecycle_manager_localization',
        output='screen',
        arguments=['--ros-args', '--log-level', log_level],
        parameters=[{'use_sim_time': use_sim_time},
                    {'autostart': autostart},
                    {'node_names': lifecycle_nodes}])


    load_hdl_localization = ComposableNodeContainer(
        name='hdl_localization_container',
        namespace=namespace,
        package='rclcpp_components',
        executable='component_container',
        composable_node_descriptions=[
            ComposableNode(
                package='hdl_localization',
                plugin='hdl_localization::GlobalmapServerNodelet',
                name='GlobalmapServerNodelet',
                parameters=[
                    {'use_sim_time': use_sim_time},
                    {'globalmap_pcd': globalmap_pcd},
                    {'downsample_resolution': 0.1},
                    {'convert_utm_to_local': True},
                ],
            ),
            ComposableNode(
                package='hdl_localization',
                plugin='hdl_localization::HdlLocalizationNodelet',
                name='HdlLocalizationNodelet',
                remappings=[
                    ('/ouster/points', points_topic),
                    ('/gpsimu_driver/imu_data', imu_topic),
                ],
                parameters=[
                    {'use_sim_time': use_sim_time},
                    {'robot_odom_frame_id': 'odom'},
                    {'odom_child_frame_id': 'base_link'},
                    {'enable_robot_odometry_prediction': False}, 
                    {'use_imu': True},
                    {'ndt_resolution': 0.5},
                    {'downsample_resolution': 0.2},
                    {'use_global_localization': False},
                    {'specify_init_pose': False},
                    {'ndt_maximum_iterations': 50},
                    {'map_intensity_threshold': 23.0}, 
                    {'ndt_neighbor_search_method': 'KDTREE'},
                    {'ndt_neighbor_search_radius': 4.0},
                    {'cool_time_duration': 1.0},
                ],
            ),
        ],
        output='screen',
        emulate_tty=True,
    )

    ld = LaunchDescription()

    ld.add_action(stdout_linebuf_envvar)

    ld.add_action(declare_namespace_cmd)
    ld.add_action(declare_map_yaml_cmd)
    ld.add_action(declare_use_sim_time_cmd)
    ld.add_action(declare_params_file_cmd)
    ld.add_action(declare_autostart_cmd)
    ld.add_action(declare_log_level_cmd)
    
    ld.add_action(declare_points_topic_cmd)
    ld.add_action(declare_imu_topic_cmd)
    ld.add_action(declare_globalmap_pcd_cmd)

    ld.add_action(load_map_server)
    
    ld.add_action(start_filter_mask_server)
    ld.add_action(start_costmap_filter_info_server)
    
    ld.add_action(load_hdl_localization)
    
    ld.add_action(load_lifecycle_manager)

    return ld